/* Copyright (C) 2017-2099 All right reserved
 * For free
 *
 * @file DLT645.cpp
 * @brief  DLT645通信协议实现代码
 * @author qigaohua, qigaohua168@163.com
 * @version v1.0
 * @date 2022-01-05
 */

#include <cstring>
#include "DLT645.h"


DLT645::DLT645(const uint8_t *addr, uint8_t ctrl, uint32_t data_id,
        uint8_t *data, size_t datalen)
{
    if (!DLT645_BuildRequestDataField(data_id, data, datalen))
        return;
    DLT645_FramePack(addr, ctrl);
}


DLT645::~DLT645()
{
    if (req  != nullptr) {
        delete req;
        std::cout << "Bye, Delete req !!!" << std::endl;
    }
    if (parse_resp != nullptr) {
        std::cout << "Bye, Delete parse_resp !!!" << std::endl;
        delete parse_resp;
    }
}


DLT645::DLT645(uint8_t *flow)
{
    DLT645_FrameUnPack(flow);
}


bool DLT645::DLT645_BuildRequestDataField(uint32_t data_id, uint8_t *data, size_t datalen)
{
    // 数据标识+数据长度+1字节检验码+1字节帧结尾符
    if (nullptr == req &&
            !DLT645_AllocFrame(sizeof(DLT645_RequestFrame)
                + sizeof(uint32_t) + datalen + sizeof(data_id) + 2,
                DLT645_REQUEST_FRAME)) {
        return false;
    }

    int i;
    uint32_t bigend_data_id = htobe32(data_id);

    for (i = 0; i < sizeof(uint32_t); ++i) {
        req->data[i] = (bigend_data_id >> (i * 8)) & 0xFF;
    }
    req->datalen += sizeof(uint32_t);

    for (i = 0; i < datalen; ++i) {
        req->data[req->datalen+i] = (*(data+i) & 0xFF);
    }
    req->datalen += datalen;

    _DLT645_Data_Add_33H(req->data, req->datalen);

    return true;
}


bool DLT645::DLT645_FramePack(const uint8_t *addr, uint8_t ctrl)
{
    uint8_t cs;

    if (nullptr == req) return false;

    req->st_c1  = DLT645_FRAME_START_CHAR;
    memcpy(req->addr, addr, DLT645_ADDR_LEN);
    req->st_c2 = DLT645_FRAME_START_CHAR;
    req->ctrl = ctrl;

    // 校验码
    cs = DLT645_FrameCS(reinterpret_cast<uint8_t*>(req),
            sizeof(DLT645_RequestFrame) + req->datalen);
    req->data[req->datalen] = cs;
    // 帧结尾符
    req->data[req->datalen+1] = DLT645_FRAME_END_CHAR;

    // == 0 表示没有帧或不是完整的数据帧
    request_frame_size_ = sizeof(DLT645_RequestFrame) + req->datalen + 2;

    return true;
}

// TODO:
// 目前仅仅只对DLT645_READ_DATA_ACK测试
bool DLT645::DLT645_VerifyFrame(uint8_t ctrl, uint8_t *data, size_t datalen)
{
    int ret = true;

    switch (ctrl) {
        case DLT645_READ_DATA_ACK:
        case DLT645_READ_DATA_HAVE_FOLLOW_ACK:
        case DLT645_READ_FOLLOW_DATA_ACK:
        case DLT645_READ_FOLLOW_DATA_HAVE_FOLLOW_ACK:
            parse_resp->dataid_len = 4;
            parse_resp->data_id = be32toh(*(uint32_t *)data);
            parse_resp->data_len = datalen - 4;
            memcpy(parse_resp->data, &data[4], parse_resp->data_len);
            break;
        case DLT645_READ_COMM_ADDR_ACK:
        case DLT645_WRITE_COMM_ADDR_ACK:
        case DLT645_REMOTE_CONTROL_ACK:
            parse_resp->dataid_len = 0;
            parse_resp->data_id = 0;
            parse_resp->data_len = datalen;
            memcpy(parse_resp->data, &data[0], parse_resp->data_len);
            break;
        case DLT645_READ_DATA_ERROR_ACK:
            std::cout << "DLT645 READ DATA ERROR ACK !" << std::endl;
            ret = false;
            break;
        case DLT645_READ_FOLLOW_DATA_ERROR_ACK:
            std::cout << "DLT645 READ FOLLOW DATA ERROR ACK !" << std::endl;
            ret = false;
            break;
        case DLT645_WRITE_DATA_ERROR_ACK:
            std::cout <<"DLT645 WRITE DATA ERROR ACK !" << std::endl;
            ret = false;
            break;
        default:
            std::cout << "INVALID DLT645 FRAME, DON'T KOWN CTRL CODE!" << std::endl;
            ret = false;
    }

    return ret;
}


bool DLT645::DLT645_FrameUnPack(uint8_t *flow)
{
    DLT645_ResponseFrame *frame = reinterpret_cast<DLT645_ResponseFrame*>(flow);
    uint8_t cs = 0;

    if (nullptr == parse_resp
            && !DLT645_AllocFrame(sizeof(DLT645_RespParseFrame_t),
                DLT645_RESPONSE_FRAME)) {
       parse_resp->status = DLT645_OOM;
       return false;
    }

    if (frame->st_c1 != DLT645_FRAME_START_CHAR
            || frame->st_c2 != DLT645_FRAME_START_CHAR) {
        parse_resp->status = DLT645_ERROR_START_CHAE;
        return false;
    }

    cs = DLT645_FrameCS(&frame->st_c1,
            sizeof(DLT645_ResponseFrame) + frame->datalen);
    if (cs != frame->data[frame->datalen]) {
        parse_resp->status = DLT645_ERROR_CS;
        return false;
    }

    if (frame->data[frame->datalen+1] != DLT645_FRAME_END_CHAR) {
        parse_resp->status = DLT645_ERROR_END_CHAE;
        return false;
    }

    _DLT645_Data_Sub_33H(frame->data, frame->datalen);

    if (!DLT645_VerifyFrame(frame->ctrl, frame->data, frame->datalen)) {
        parse_resp->status = DLT645_ERROR_CTRL_CODE;
        return false;
    }

    // 这里只支持2007
    parse_resp->protocol = DLT645_PROTOCOL_2007;

    memcpy(parse_resp->addr, frame->addr, DLT645_ADDR_LEN);
    parse_resp->ctrl = frame->ctrl;

    return true;
}





#define TEST
#ifdef TEST

#include <ctype.h>
static inline void dump_line(FILE * out, const char *addr, const long len)
{
	int i;
	fprintf(out, "|");
	for (i = 0; i < 16; i++) {
		if (i < len) {
			fprintf(out, " %02X", ((uint8_t *) addr)[i]);
		} else {
			fprintf(out, " ..");
		}
		if (!((i + 1) % 8)) {
			fprintf(out, " |");
		}
	}
	fprintf(out, "\t");
	for (i = 0; i < 16; i++) {
		char c = 0x7f & ((uint8_t *) addr)[i];
		if (i < len && isprint(c)) {
			fprintf(out, "%c", c);
		} else {
			fprintf(out, ".");
		}
	}
}


__attribute__((unused))
static void dump(FILE * out, const char *addr, const long len)
{
	int i;
	for (i = 0; i < len; i += 16) {
		dump_line(out, addr + i, (len - i) < 16 ? (len - i) : 16);
		fprintf(out, "\n");
	}
	fflush(out);
}


int main()
{

    uint8_t addr[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
    uint8_t data[5] = {0x11, 0x22, 0x33, 0x44, 0x55};

    {
        DLT645 dlt;
        dlt.DLT645_BuildRequestDataField(0x01020304, nullptr, 0);
        dlt.DLT645_FramePack(addr, 0x99);
        dump(stdout, (const char *)dlt.DLT645_GetRequestFlow(), dlt.DLT645_GetRequestFrameLength());
    }

    std::cout << "=====================================" << std::endl;

    {
        DLT645 req1(addr, 0x99, 0x01020304, data, 5);
        DLT645 req2(addr, 0x99, 0x01020304);
        dump(stdout, (const char *)req1.DLT645_GetRequestFlow(), req1.DLT645_GetRequestFrameLength());
        dump(stdout, (const char *)req2.DLT645_GetRequestFlow(), req2.DLT645_GetRequestFrameLength());
    }

    std::cout << "=====================================" << std::endl;

    {
        DLT645 req1(addr, DLT645_READ_DATA_ACK, 0x01020304, data, 5);
        uint8_t *flow = req1.DLT645_GetRequestFlow();

        std::cout << int(req1.DLT645_GetResponseFrameState()) << std::endl;
        dump(stdout, (const char *)req1.DLT645_GetRequestFlow(), req1.DLT645_GetRequestFrameLength());

        req1.DLT645_FrameUnPack(flow);
        std::cout << "state: " << int(req1.DLT645_GetResponseFrameState()) << std::endl;
        std::cout << std::hex;
        std::cout << "ctrl:  " << (int)req1.DLT645_GetResponseFrameCtrlCode() << std::endl;
        std::cout << "data_id: " << (int)req1.DLT645_GetResponseFrameDataId() << std::endl;
        uint8_t *data;
        uint8_t datalen;
        req1.DLT645_GetResponseFrameData(&data, &datalen);
        for (int i = 0; i < datalen; ++i) {
            printf("%02X\t", data[i]);
        }
        std::cout << std::endl;
    }

    std::cout << "=====================================" << std::endl;

    {
        DLT645 req1(addr, DLT645_READ_DATA_ACK, 0x01020304, data, 5);
        uint8_t *flow = req1.DLT645_GetRequestFlow();

        DLT645 resp(flow);
        std::cout << "state: " << int(resp.DLT645_GetResponseFrameState()) << std::endl;
        std::cout << std::hex;
        std::cout << "ctrl:  " << (int)resp.DLT645_GetResponseFrameCtrlCode() << std::endl;
        std::cout << "data_id: " << (int)resp.DLT645_GetResponseFrameDataId() << std::endl;
        uint8_t *data;
        uint8_t datalen;
        resp.DLT645_GetResponseFrameData(&data, &datalen);
        for (int i = 0; i < datalen; ++i) {
            printf("%02X\t", data[i]);
        }
        std::cout << std::endl;
    }

    return 0;
}




#endif
